/* 数据库session表引擎，事务处理，查询执行。  table.go包访问session.go包,session.go包操作数据库  ***/
package db

import (
	"database/sql"
	"fmt"
	"log"
	"os"
	"strings"
	"sync"
	"time"
)

/*表引擎*/
type Session struct {
	db        *sql.DB     //普通
	tx        *sql.Tx     //事务
	conf      *Config     //配置
	transOpen bool        //事务开启状态
	loglock   *sync.Mutex //日志锁
}

//导出数据库
func (this *Session) Dump(table string) {

}

//写入sql数据
func (this *Session) Load(sql string) {

}

//开启事务
func (this *Session) Begin() (e error) {
	defer this.writeLogAndDebug(true, time.Now(), "BEGIN")
	this.transOpen = true
	this.tx, e = this.db.Begin()
	return e
}

//事务提交
func (this *Session) Commit() error {
	defer this.writeLogAndDebug(true, time.Now(), "COMMIT")
	this.transOpen = false
	return this.tx.Commit()
}

//事务回滚
func (this *Session) RollBack() error {
	defer this.writeLogAndDebug(true, time.Now(), "ROLLBACK")
	this.transOpen = false
	return this.tx.Rollback()
}

//biu组合查询
func (this *Session) Table(tablename string) *Table {
	return newTable(this, tablename)
}

//query
func (this *Session) Query(query string, args ...interface{}) (result []map[string]string, err error) {
	defer this.writeLogAndDebug(false, time.Now(), query, args...)
	var rows *sql.Rows
	if this.transOpen {
		rows, err = this.tx.Query(query, args...)
	} else {
		rows, err = this.db.Query(query, args...)
	}
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	columns, err := rows.Columns()
	if err != nil {
		return nil, err
	}
	columnsLen := len(columns)
	values := make([]sql.RawBytes, columnsLen)
	scans := make([]interface{}, columnsLen)
	for i := range values {
		scans[i] = &values[i]
	}
	for rows.Next() {
		rows.Scan(scans...)
		column := make(map[string]string, 0)
		for i, col := range values {
			column[columns[i]] = string(col)
		}
		result = append(result, column)
	}
	return
}

//exec
func (this *Session) Exec(query string, args ...interface{}) (sql.Result, error) {
	defer this.writeLogAndDebug(true, time.Now(), query, args...)
	if this.transOpen {
		return this.tx.Exec(query, args...)
	}
	return this.db.Exec(query, args...)
}

//异步写日志或者调试输出
func (this *Session) writeLogAndDebug(exec bool, t time.Time, query string, args ...interface{}) {
	if !this.conf.Debug && this.conf.LogFile == "" {
		return
	}
	go func() {
		if this.loglock == nil {
			this.loglock = new(sync.Mutex)
		}
		this.loglock.Lock()
		defer this.loglock.Unlock()
		now := time.Now()
		useTime := (now.UnixNano() - t.UnixNano()) / 1000000
		//生成sql语句
		queryArray := strings.Split(query, "?")
		queryData := make([]interface{}, 0)
		l := len(args)
		for k, v := range queryArray {
			queryData = append(queryData, "\"", v, "\"")
			if l > k {
				queryData = append(queryData, args[k])
			}
		}
		query = fmt.Sprint(queryData...)
		//
		if this.conf.Debug {
			log.Printf("[sql:%s][%d ms]", query, useTime)
			//打印输出sql语句
		}
		//写日志只写 insert,update,delete
		if this.conf.LogFile != "" && exec {
			file, e := os.OpenFile(this.conf.LogFile, os.O_CREATE|os.O_APPEND|os.O_RDWR, 0666)
			if e != nil {
				panic(e)
			}
			defer file.Close()
			file.WriteString("\n")
			file.WriteString(now.Format("[2006-01-02 15:04:05]\t>\t"))
			file.WriteString(query)
		}
	}()
}
